package vision.nvhuong.service;

import java.util.concurrent.Callable;

import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.PersistenceException;

import ch.ivyteam.ivy.environment.Ivy;
import ch.ivyteam.ivy.process.data.persistence.IIvyEntityManager;
import ch.ivyteam.ivy.security.SecurityManagerFactory;
import vision.nvhuong.dao.PersistenceExecutable;

/**
 * This class is responsible for managing transaction 
 * @author nvhuong
 *
 */
public  class TransactionManager<T> {
	private String persistenceUnitName = "helloivy_unit";	
	private IIvyEntityManager entityManager = Ivy.persistence().get(
			persistenceUnitName);
	
	protected T execute(String operationName, boolean needsTransaction,
			PersistenceExecutable<T> executable) {
		EntityManager em = null;
		try {
			em = entityManager.createEntityManager();
		} catch (Exception ex) {
			throw new PersistenceException("Failed to excecute operation \""
					+ operationName + "\" for persistence unit \""
					+ this.persistenceUnitName + "\".", ex);
		}
		return execute(operationName, em, needsTransaction, executable);
	}
	
	@SuppressWarnings("unchecked")
	private T execute(String operationName, final EntityManager em,
			final boolean needsTransaction,
			final PersistenceExecutable<T> executable) {
		try {
			return SecurityManagerFactory.getSecurityManager().executeAsSystem(
					new Callable<T>() {
						public T call() throws Exception {
							EntityTransaction transaction = em.getTransaction();
							EntityTransaction finalTransaction = transaction;

							boolean beginTransaction = (needsTransaction)
									&& (!finalTransaction.isActive());

							if (beginTransaction) {
								finalTransaction.begin();
							}

							Object result = executable.execute(em);

							if (beginTransaction) {
								finalTransaction.commit();
							}
							return (T) result;
						}

					});
		} catch (Exception ex) {
			Ivy.log()
					.error("Failed to excecute operation \"{0}\" for persistence unit \"{1}\".",
							ex,
							new Object[] { operationName,
									this.persistenceUnitName });

			EntityTransaction transaction = em.getTransaction();
			if (transaction.isActive()) {
				try {
					transaction.rollback();
				} catch (Exception rollbackEx) {
					Ivy.log()
							.error("Failed to rollback transaction for persistence unit helloivy_unit");
				}
			}
			if ((ex instanceof RuntimeException)) {
				throw ((RuntimeException) ex);
			}

			throw new PersistenceException("Failed to excecute operation \""
					+ operationName + "\" for persistence unit \""
					+ this.persistenceUnitName + "\".", ex);
		} finally {
			if (em != null) {
				if (!em.getTransaction().isActive()) {
					em.close();
				}
			}
		}
	}

}
